package dk.jwood.android.graph;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import android.graphics.Color;
import android.util.Log;

public class DataProvider implements Iterable<DiscreetValue> {

	private static final String TAG = "DataProvider";

	private final DataProviderManager manager;
	
	private final String id;
	
	private final List<ValueDescriptorImpl> valueDescriptors;
	
	
	private String name;
	


	private transient Node latest;
	private transient Node earliest;
	
	private transient int count = 0;
	
	private List<DataProviderListener> listeners;
	
	
	private int maxValues = 80;
	
	
	


	public void save() {
		
		manager.savePrefs(this);
		
	}
	
	
	
	public DataProvider(String id, int valueCount, DataProviderManager dataProviderManager) {
		super();
		
		this.manager = dataProviderManager;
		this.id = id;
		this.name = id;
		
		this.listeners = new LinkedList<DataProviderListener>();
		
//		this.values = new LinkedList<DiscreetValue>();
		
		if(valueCount == 1) {
			valueDescriptors = Collections.singletonList(new ValueDescriptorImpl(0));
		} else {
			List<ValueDescriptorImpl> vd = new ArrayList<DataProvider.ValueDescriptorImpl>();
			for(int i = 0; i < valueCount; i++) {
				vd.add(new ValueDescriptorImpl(i));
			}
			valueDescriptors = Collections.unmodifiableList(vd);
		}
		

	}
	
	public DataProvider(String id, List<String> valueDescNames, DataProviderManager dataProviderManager) {
		super();
		
		this.manager = dataProviderManager;
		this.id = id;
		this.name = id;
		
		this.listeners = new LinkedList<DataProviderListener>();
		
//		this.values = new LinkedList<DiscreetValue>();
		
		
		
		if(valueDescNames.size() == 1) {
			valueDescriptors = Collections.singletonList(new ValueDescriptorImpl(0, valueDescNames.get(0)));
		} else {
			List<ValueDescriptorImpl> vd = new ArrayList<DataProvider.ValueDescriptorImpl>();
			for(int i = 0; i < valueDescNames.size(); i++) {
				vd.add(new ValueDescriptorImpl(i, valueDescNames.get(i)));
			}
			valueDescriptors = Collections.unmodifiableList(vd);
		}
		
		
	}

	public synchronized void addDataPoint(long timestamp, float[] newValues) {

//		Log.d(TAG, "addDataPoint...");
		
//		Log.d(TAG, "addDataPoint for "+getId()+": currentCount: "+count+" "+Arrays.asList(newValues));
		
		if(newValues == null || newValues.length != valueDescriptors.size()) {
			throw new IllegalArgumentException("wrong value count: is "+(newValues == null ? "NULL" : newValues.length)+" should be "+valueDescriptors.size());
		}
		
		if(latest == null || latest.value.getTimestamp() < timestamp) {
			DiscreetValue dp = new DiscreetValue(timestamp, newValues);
			Node node = new Node(dp, latest);
			latest = node;
			
			if(earliest == null) earliest = latest;

			count++;
			
			while(count > maxValues) {
				Node delete = earliest;
				earliest = delete.next;
				earliest.previous = null;
				delete.next = null;
				count--;
//				Log.d(TAG, "removing dp "+delete);
			}
			
			notifyListeners(dp);
		}
		
		
	}
	
	private final void notifyListeners(final DiscreetValue dp) {
		if(listeners.isEmpty()) return;
		for(DataProviderListener listener : listeners) {
			listener.pointAdded(dp);
		}
		
		
	}
	
	
	public int getMaxValues() {
		return maxValues;
	}



	public void setMaxValues(int maxValues) {
		this.maxValues = maxValues;
	}


	public String getId() {
		return id;
	}


//	List<DiscreetValue> getDataPoints() {
//		return this.values;
//	}
	
	public Iterator<DiscreetValue> iterator() {
		
		return new NodeIterator(latest, false);
		
//		return values.iterator();
	}
	
	public boolean isEmpty() {
		
		return this.latest == null;
		
	}
	
	
	public void addListener(DataProviderListener listener) {
		if(!listeners.contains(listener)) {
			listeners.add(listener);
		}
	}
	
	public void removeListener(DataProviderListener listener) {
		listeners.remove(listener);
	}
	
	
	
	public interface ValueDescriptor {
		
		public int getIndex();
		public String getLabel();
		public void setLabel(String label);
		
		public int getColor();
		public void setColor(int color);
		
	}
	
	public DiscreetValue getMean(int count) {
		
		float[] total = new float[getValuesCount()];
		int seen = 0;
		for(DiscreetValue dv : this) {
			for(int i = 0; i < getValuesCount(); i++) {
				total[i] += dv.getValues()[i];
			}
			seen++;
			if(seen > count) break;
		}

		for(int i = 0; i < getValuesCount(); i++) {
			total[i] = total[i] / (seen - 1);
		}
		
		DiscreetValue result = new DiscreetValue(this.latest.value.getTimestamp(), total);
		
		return result;
		
	}
	
	
	public static class ValueDescriptorImpl implements ValueDescriptor {
		
		private final int index;
		
		private String label;
		
		private int color;
		
		
		

		public ValueDescriptorImpl(int index) {
			this(index, "v"+index);
		}
		
		public ValueDescriptorImpl(int index, String label) {
			this.index = index;
			this.label = label;
		}

		public int getColor() {
			return color;
		}

		public void setColor(int color) {
			this.color = color;
		}

		public final String getLabel() {
			return label;
		}

		public final void setLabel(String label) {
			this.label = label;
		}

		public final int getIndex() {
			return index;
		}
		
	}
	
	public interface DataProviderListener {
		
		void pointAdded(DiscreetValue dp);
		
		
	}

	protected final List<ValueDescriptorImpl> getValueDescriptors() {
		return valueDescriptors;
	}
	
	public final ValueDescriptor getValueDescriptor(int index) {
		return valueDescriptors.get(index);
	}

	public String getName() {
		return name;
	}



	public void setName(String name) {
		this.name = name;
	}


	/**
	 * return # of datapoints in each descreet point
	 * 
	 * @return
	 */
	public int getValuesCount() {
		return valueDescriptors.size();
	}

	
	private class Node {

		private final DiscreetValue value;

		private Node next;
		private Node previous;
		
		
		public Node(DiscreetValue value, Node previous) {
			super();
			this.previous = previous;
			this.value = value;
			
			if(previous != null) previous.next = this;
			
		}

		
		
		
		
	}
	
	
	private final class NodeIterator implements Iterator<DiscreetValue> {
		
		private final boolean forward;
		
		private Node next;
		
		public NodeIterator(Node from, boolean forward) {
		
			this.forward = forward;
			
			this.next = from;
			
		}

		@Override
		public final boolean hasNext() {
			return next != null;
		}

		@Override
		public final DiscreetValue next() {
			Node result = next;
			
			next = forward ? result.next : result.previous;
			
			return result.value;
		}

		@Override
		public final void remove() {
			
		}
		
		
		
	}
	
	

}
